package edu.buaa.act.anonymous.coarseGrained;

import edu.buaa.act.helper.Utils;
import edu.buaa.act.model.GPSPoint;
import org.slf4j.Logger;

import java.io.IOException;

/**
 * Created by song on 2017/6/14 0014.
 */
public class RegionMetrics
{
    static private final Logger log = Utils.getDebugLogger();

    static private final double R = 6371000.0D;

    private final long minT;
    private final double minLon;
    private final double minLat;

    private final long timeCount;
    private final long lonCount;
    private long latCount = 0;

    private final double dLat;
    private final double dLon;
    private final int tInt; // time interval

    public RegionMetrics(double minLon, double maxLon, double minLat, double maxLat, long minTime, long maxTime, int length, int t){
        this.minLon = minLon;
        this.minLat = minLat;
        this.minT = minTime;
        this.tInt = t;

        this.dLat = (180 * length) / (Math.PI * R);
        this.dLon = dLonAtLat( (minLat + maxLat)/2, length );

        this.latCount = (long) Math.ceil( (maxLat - minLat)/dLat );
        this.lonCount = (long) Math.ceil( (maxLon - minLon)/dLon );
        this.timeCount = (long) Math.ceil( (maxTime - minTime)/t );

        log.trace("dLat({}) dLon({}) latCount({}) lonCount({}) timeCount({}) regionCount({})",
                  dLat, dLon, latCount, lonCount, timeCount, latCount *lonCount*timeCount);
    }

    private double dLonAtLat( double lat, int length ) {
        double latRadians = lat * Math.PI / 180;
        double r  = R * Math.abs(Math.cos(latRadians));
        return 180d * length / (Math.PI * r);
    }

    public long next(GPSPoint p) throws IOException {
        double deltaLat = p.getLat() - minLat;
        long latIndex = (long) Math.floor( deltaLat / dLat );

        double deltaLon = p.getLon() - minLon;
        long lonIndex = (long) Math.floor( deltaLon / dLon );

        long deltaTime = p.getTime() - minT;
        long timeIndex = deltaTime/tInt;

        if( 0<latIndex && latIndex<latCount &&
            0<lonIndex && lonIndex<lonCount &&
            0<timeIndex && timeIndex<timeCount) {
                // it seem that if this condition is true, then the generated id is
                // unique for any unique triple(latIndex, lonIndex, timeIndex)
            long id = latIndex * (lonCount * timeCount) + lonIndex * (timeCount) + timeIndex;
            return id;
        }else{
            throw new RuntimeException("latitude or longitude or time index out of range: "+String.format("lat(%d/%d) lon(%d/%d) time(%d/%d)", latIndex, latCount, lonIndex, lonCount, timeIndex, timeCount));
        }
    }
}
